#pragma once

#include <boost/static_assert.hpp>
#include <boost/iterator/filter_iterator.hpp>




namespace boostext
{
	/**
	 * This is the default predicate for the access policies of a resource view.
	 */
	class no_filter
	{
	public:

		template <typename T>
		bool operator()(const T& that)
		{ return true; }
	};
	///////////////////////////////////////////////////////////////////////////////////////////////////




	/**
	 * This is the base class for any access policy.
	 * It contains some necessary defines and stores iterators to
	 * the sequence it manages access to.
	 */
	template
	<
		typename internal_iterator,
		typename internal_const_iterator,
		typename Predicate
	>
	class access_policy_base
	{
	public:

		typedef boost::filter_iterator<Predicate, internal_iterator>         iterator;
		typedef boost::filter_iterator<Predicate, internal_const_iterator>   const_iterator;

	private:

		iterator   m_begin;
		iterator   m_end;

	protected:

		/**
		 * Create an access_policy_base from a range of elements.
		 * This function is only available when the predicate supports
		 * default construction.
		 *
		 * @param begin   iterator to the first element in the sequence
		 * @param end     iterator past the last element in the sequence
		 */
		template <typename Iter>
		access_policy_base(Iter begin, Iter end)
			// For details about the filter_iterator, go to
			// www.boost.org/doc/libs/1_42_0/libs/iterator/doc/filter_iterator.html
			: m_begin(begin, end)
			, m_end(end, end)
		{}

		/**
		 * Create an access_policy_base from a range of elements and a predicate.
		 *
		 * @param pred    the filter that is applied to the view
		 * @param begin   iterator to the first element in the sequence
		 * @param end     iterator past the last element in the sequence
		 */
		template <typename Iter>
		access_policy_base(const Predicate& pred, Iter begin, Iter end)
			: m_begin(pred, begin, end)
			, m_end(pred, end, end)
		{}

	public:

		/**
		 * Get an iterator to the first element in the sequence.
		 */
		iterator begin() { return m_begin; }

		/**
		 * Get an iterator past the last element in the sequence.
		 */
		iterator end() { return m_end; }



		/**
		 * Get an iterator to the first element in the sequence.
		 */
		const_iterator begin() const { return m_begin; }

		/**
		 * Get an iterator past the last element in the sequence.
		 */
		const_iterator end() const { return m_end; }



		/**
		 *Filter this view, based on a certain predicate.
		 */
		void filter(Predicate pred)
		{
			m_begin = iterator(pred, m_begin.base(), m_end.base());
			m_end   = iterator(pred, m_end.base(), m_end.base());
		}
	};
	///////////////////////////////////////////////////////////////////////////////////////////////////




	/**
	 * This access policy grants unlimited access to a resource.
	 * It is the default policy for a resource_view.
	 *
	 * As with any other policy, an optional predicate can be defined that acts as a filter
	 * on the elements of the sequence, skipping those that are of no interest.
	 */
	template
	<
		typename storage_policy,
		typename Predicate
	>
	class unlimited_access
		: public access_policy_base<typename storage_policy::iterator, typename storage_policy::const_iterator, Predicate>
	{
	protected:

		template <typename Iter>
		unlimited_access(Iter begin, Iter end)
			: access_policy_base(begin, end)
		{}

		template <typename Iter>
		unlimited_access(const Predicate& pred, Iter begin, Iter end)
			: access_policy_base(pred, begin, end)
		{}
	};
	///////////////////////////////////////////////////////////////////////////////////////////////////




	/**
	 * This access policy grants read-only access to a resource.
	 *
	 * As with any other policy, an optional predicate can be defined that acts as a filter
	 * on the elements of the sequence, skipping those that are of no interest.
	 */
	template
	<
		typename storage_policy,
		typename Predicate
	>
	class const_access
		: public access_policy_base<typename storage_policy::const_iterator, typename storage_policy::const_iterator, Predicate>
	{
	protected:

		template <typename Iter>
		const_access(Iter begin, Iter end)
			: access_policy_base(begin, end)
		{}

		template <typename Iter>
		const_access(Predicate pred, Iter begin, Iter end)
			: access_policy_base(pred, begin, end)
		{}
	};
	///////////////////////////////////////////////////////////////////////////////////////////////////




	/**
	 * This access policy provides access to a certain range of elements.
	 * The policy can only be applied if the value_type of the given container
	 * is totally ordered.
	 *
	 * A view created with this policy provides access to all elements, equal to
	 * a default constructed value_type: It must be set explicitly by calling setRange.
	 */
	template
	<
		typename storage_policy,
		typename Predicate
	>
	class range_access
		: public access_policy_base<typename storage_policy::iterator, typename storage_policy::const_iterator, Predicate>
	{
	public:

		BOOST_STATIC_ASSERT( storage_policy::totally_ordered == true );
		BOOST_STATIC_ASSERT( storage_policy::ascending == true );

		typedef typename storage_policy::value_type   value_type;

	private:

		value_type   m_lowerEnd;
		value_type   m_upperEnd;

		iterator     m_viewBegin;
		iterator     m_viewEnd;

	protected:

		template <typename Iter>
		range_access(Iter begin, Iter end)
			: access_policy_base(begin, end)
			, m_viewBegin(first(m_lowerEnd))
			, m_viewEnd(last(m_upperEnd))
		{}

	public:

		iterator begin() { return m_viewBegin; }
		iterator nd() { return m_viewEnd; }

		const_iterator begin() const { return m_viewBegin; }
		const_iterator end() const { return m_viewEnd; }

	private:

		iterator first(const value_type& lowerEnd)
		{
			iterator i = __super::begin();
			while(i != __super::end())
				if(*i >= lowerEnd)
					break;

			return i;
		}

		iterator last(const value_type& upperEnd)
		{
			iterator i = __super::end();
			if(i == __super::begin())
				return i;

			while(i != __super:begin())
			{
				--i;

				if(*i <= upperEnd)
					break;
			}

			return i;
		}

	public:

		/**
		 * Set the range of elements that should be visible by a sequence_view.
		 */
		void setRange(const value_type& lowerEnd, const value_type& upperEnd)
		{
			if(lowerEnd > upperEnd)
				m_lowerEnd = upperEnd, m_upperEnd = lowerEnd;
			else
				m_lowerEnd = lowerEnd, m_upperEnd = upperEnd;

			m_viewBegin = first(m_lowerEnd);
			m_viewEnd   = last(m_upperEnd);
		}
	};
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
